<script setup>
import {computed, h, nextTick, onUnmounted, reactive, ref, toRaw, watch} from 'vue'
import {useRouter} from "vue-router";
import {storeToRefs} from 'pinia'
import {NButton, NCode, NText, useMessage} from 'naive-ui'
import {useUserProfile} from "@/stores/useUserProfile.js";
import {fuzzyMatch} from "@/utils/fuzzy.js";
import {FitToScreen} from "@vicons/carbon"
import {renameDuplicates} from "@/utils/dataHandler.js";


// 创建数据广播通道
const channel = new BroadcastChannel('telemetryChannel');
// 获取pinia中的配置以及最新的遥测
const {profile, latestTelemetryData, selectedVersion} = storeToRefs(useUserProfile());
const {telemetryConfig, setUserProfile} = useUserProfile()
const props = defineProps({
	// 仅筛选出传入的遥测 -> ["帧头", "相机温度"]
	labels: {
		type: Array,
		required: false,
		default: []
	},
	// 需要排除不展示在遥测表格中的列
	excludesColumns: {
		type: Array,
		required: false,
		default: []
	},
	// 类样式
	"class": {
		type: String,
	},
	// 是否过滤
	isFilter: {
		type: Boolean,
		default: false
	},
	// 是否为新标签页
	isNewWindows: {
		type: Boolean,
		default: false
	},
	// 使用提供的数据
	data: {
		type: Object,
		default: void 0
	},
	// 当前遥测设备
	device: {
		type: String,
		default: {"asLabel": "optical", name: "光学头"}
	},
	// 最后刷新时间
	lastTime: {
		type: String
	},
	// 显示的bar项目
	showBars: {
		type: Array,
		default: ["search", "screenshot", "pop", "diy_telemetry", "version", "time"]
	}
})

// 表格对象ref
const tableRef = ref(null)
// 快捷查询的字符串
const queryString = ref(null)
// 当表格有数据时渲染
const isTableRendered = computed(() => {
	return mutableData.value && mutableData.value.length > 0
})

// 监听用户对filterList的修改,自定义遥测时触发
watch(() => profile.value.filterList, (newVal) => {
	colsRef.value[1].filterOptions = newVal
})


// 构造的表格列
const colsRef = ref([
	{
		title: "序号",
		key: "index",
		width: "2%",
		className: "handle",
	},
	{
		title: "信号名称",
		key: "label",
		width: "14%",
		render: (row) => {
			return h(
				NText,
				{"class": "text-base font-thin"},
				// {"class": "p-2 bg-zinc-800 rounded-sm"},
				{
					default: () => `${row.label}(${row.bitLength}bit)`
				}
			)
		},
		filterMultiple: false,
		// 选择基于什么筛选,例如温度
		filterOptionValue: null,
		sorter: 'default',
		// 所有筛选项
		filterOptions: [...profile.value.filterList],
		// 筛选函数
		filter(value, row) {
			if (value.includes("指令跟随")) {
				return props.labels.includes(row.label)
			} else if (value.includes("-")) {
				console.log(value.split("-"))
				// 代表是自定义的遥测
				return value.split("-").includes(row.label)
			} else {
				// 包含
				return !!~row.label.indexOf(value.toString())
			}
		}
	},
	{
		title: "显示值",
		key: "value",
		width: "10%",
		sorter: 'default',
		render: (row) => {
			return h(
				NCode,
				{
					"class": [
						// 对一些0x1和0x0的状态做显示优化
						"p-2 text-base bg-zinc-800 rounded-sm select-text whitespace-nowrap",
						parseInt(row.sourceCode) === 1 && row.value !== 1 ? "text-green-300" : "",
						parseInt(row.sourceCode) === 0 && row.value !== 0 ? "text-red-500" : "",
					].join(" ")
				},
				{
					default: () => row.value !== null && row.value !== undefined ? row.value : "<未定义|字节解析错误>"

				}
			)
		}
	},
	{
		title: "源码值",
		key: "sourceCode",
		width: "6.5%"
	},
	{
		title: "操作",
		key: "actions",
		width: "7%",
		render(row) {
			return h(
				'div',  // 可以使用 div 或 Fragment 来包裹多个按钮
				{"class": "flex items-center gap-2"},
				[
					h(
						NButton,
						{
							strong: true,
							tertiary: true,
							size: 'small',
							onClick: () => copy(row)
						},
						{default: () => '复制值'}
					),
					h(
						NButton,
						{
							strong: true,
							tertiary: true,
							size: 'small',
							onClick: () => highlighter(row)
						},
						{default: () => '高亮标记'}
					)
				]
			);
		},

	}
])
// 构造出的遥测列会动态感知colsRef中的变化
const telemetryColumns = computed({
	get() {
		// 如果设置了过滤字段, 则会被过滤
		return props.isFilter ? colsRef.value.filter(item => !props.excludesColumns.includes(item.title)) : colsRef.value
	}, set(col) {
		// 找到对应修改的列
		const objIndex = colsRef.value.findIndex(item => item.key === col.key)
		colsRef.value[objIndex] = col
	}
})


// 选择筛选后会刷新当前表格,只显示筛选后的行
function handleUpdateFilter(filters, sourceColumn) {
	telemetryColumns.value = {...sourceColumn, filterOptionValue: filters[sourceColumn.key]};
}

const message = useMessage()
// 自定义遥测的弹窗
const showModal = ref(false)

// 穿梭框选项(提供所有信号值): 用于用户筛选遥测
const transferOptions = computed(() => {
	if (props.device.asLabel === "optical") {
		return renameDuplicates(telemetryConfig.optical.telemetry.map(
			item => ({label: item.name, value: item.name})
		), "label")
	} else {
		return []
	}
})
// 自定义筛选
const localFilter = reactive({
	label: "",
	value: []
})
const handleClose = (label) => {
	// 获取原筛选列表
	let filterList = profile.value.filterList
	const lastIndex = filterList.findLastIndex(item => item.label === label)
	filterList.splice(lastIndex, 1)
	setUserProfile("filterList", filterList)
}
const handlePositive = () => {
	// 用户点击,则加入筛选项(先存储到pinia)
	setUserProfile("filterList", [...profile.value.filterList, {...localFilter, value: localFilter.value.join("-")}])
}

// 被高亮显示的行
const highlighterList = ref([])

function rowClassName(row) {
	if (highlighterList.value.includes(row.label)) {
		return 'brown'
	}
	return ''
}

// 高亮标记函数
const highlighter = (row) => {
	// 存在 -> 删除
	if (highlighterList.value.includes(row.label)) {
		highlighterList.value.splice(highlighterList.value.indexOf(row.label), 1)
		// 不存在 -> 添加
	} else {
		highlighterList.value.push(row.label)
	}
}

// 复制值到剪贴板
const copy = async (row) => {
	await navigator.clipboard.writeText(row.value)
	message.info(`复制了[${row.label}]的显示值`)
}

// 表格数据
const mutableData = ref([])
// 信号选项
const signalOptions = ref([])
// 监听第一次遥测的标签,使其转化为非响应式数据
const updateLabel = watch(mutableData, (newVal) => {
	if (!mutableData) return []
	signalOptions.value = toRaw(newVal?.map(item => ({label: item.label, value: item.label})))
	updateLabel()
})

// 不断监视遥测变化
const updateTableData = (newValue) => {
	// 如果长度不对其则代表遥测版本切换过
	// 先保存要更新的值
	let updatedData = newValue ? updateArrayByKey(
		mutableData.value,
		newValue.parsedData?.map((item, index) => ({
			...item,
			index: index + 1
		})),
		"index"
	) : [];

	// 渲染帧空闲时间进行表格数据刷新
	requestIdleCallback((idle) => {
		if (idle.timeRemaining() > 0 && !document.hidden) {
			mutableData.value = updatedData;
			// **确保 `mutableData.value` 更新后再发送**
			channel.postMessage(toRaw(mutableData.value));
		} else {
			console.log("时间不足");
			// 如果时间不足，立即同步数据
			mutableData.value = updatedData;
			channel.postMessage(toRaw(mutableData.value));
		}
	});
};


// 当组件props传递的isNewWindow为false时才使用此方法更新遥测
watch(latestTelemetryData, updateTableData, {immediate: true});

if (props.isNewWindows) {
	// Telemetry子组件
	watch(() => props.data, (newVal) => {
		mutableData.value = newVal
	})
}


/**
 * 更新原数组中的对象值，保持原数组顺序
 * @param {Array} originalArray - 原数组
 * @param {Array} updateArray - 更新数据的数组
 * @param {string} key - 用于匹配的键名
 * @returns {Array} - 更新后的数组
 */
function updateArrayByKey(originalArray, updateArray, key) {
	if (!originalArray || originalArray.length === 0) {
		return updateArray;
	}
	if (originalArray.length !== updateArray.length) {
		return updateArray
	}

	const updateMap = new Map(updateArray.map(item => [item[key], item]));

	return originalArray.map(item => updateMap.get(item[key]) || item);
}


/**
 * 过滤函数,输入时触发,返回过滤后的列表
 * @param patter 输入的查询字符串
 * @param option 选项
 * @returns {boolean}
 */
const intoViewByFilter = (patter, option) => {
	console.time("intoViewByFilter")
	const result = fuzzyMatch(signalOptions.value, "label", patter);
	const labelSet = new Set(result.map(item => item.label));
	console.timeEnd("intoViewByFilter")
	return labelSet.has(option.label);
};


/**
 * 进入行, 被快捷查询功能触发,会自动滚动到搜索到的行,并高亮标记.
 * @param value
 */
const intoRow = (value) => {
	const obj = mutableData.value.filter(item => item.label === value)[0]
	tableRef.value.scrollTo({
		index: obj.index - 1
	});
	highlighter(obj)
}

// 打开一个新的标签页展示遥测
const newTWindows = () => {
	window.open("/TelemetryWindow")
}


/**
 * 截取屏幕流(非局部截取)
 * @returns {Promise<void>}
 */
const screenshot = async () => {
	try {
		// 获取屏幕媒体流
		const stream = await navigator.mediaDevices.getDisplayMedia({video: true});

		// 创建视频元素
		const video = document.createElement("video");
		video.srcObject = stream;
		video.play();

		video.onloadedmetadata = async function () {
			// 获取视频宽高
			const width = video.videoWidth;
			const height = video.videoHeight;

			// 创建 Canvas
			const canvas = document.createElement("canvas");
			canvas.width = width;
			canvas.height = height;
			const ctx = canvas.getContext("2d");

			// 绘制视频帧到 Canvas
			ctx.drawImage(video, 0, 0, width, height);

			// 停止视频流
			stream.getTracks().forEach(track => track.stop());

			// 生成下载链接
			canvas.toBlob(blob => {
				const a = document.createElement("a");
				a.href = URL.createObjectURL(blob);
				a.download = "screenshot.png";
				document.body.appendChild(a);
				a.click();
				document.body.removeChild(a);
			}, "image/png");
		};
	} catch (error) {
		console.error("截图失败:", error);
	}
}

// 右击菜单
const options = [
	{
		label: "弹出图表",
		key: "popChart"
	},
	{
		label: () => h("span", {}, "条件标记"),
		key: "conditionTag"
	}
];
const showDropdown = ref(false)
const xRef = ref(null)
const yRef = ref(null)
const handleSelect = () => {
	showDropdown.value = false;
}
const onClickoutside = () => {
	showDropdown.value = false;
}
const rowProps = (row) => {
	return {
		onContextmenu: (e) => {
			// 右键菜单
			e.preventDefault();
			showDropdown.value = false;
			nextTick().then(() => {
				showDropdown.value = true;
				xRef.value = e.clientX;
				yRef.value = e.clientY;
			});
		}
	};
}

const router = useRouter()
onUnmounted(() => {
	channel.close();
	channel.onmessage = null
});

</script>

<template>
	<div class="h-full flex flex-col gap-1">
		<!--可拖拽行的表格如果遥测数据未加载会报错el-->
		<n-space class="w-full py-0.5" align="center" size="small" :wrap-item="false" :wrap="false">
			<n-select class="w-auto" v-model:value="queryString"
					  filterable
					  :filter="intoViewByFilter"
					  placeholder="输入信号名称查询"
					  :options="signalOptions"
					  label-field="label" value-field="label"
					  @update:value="intoRow"
			></n-select>
			<n-button secondary type="error" @click="screenshot">记录快照</n-button>
			<n-button secondary type="info" class="flex text-base flex-row-reverse" @click="newTWindows">
				<n-icon :component="FitToScreen" size="19"></n-icon>
				弹出
			</n-button>
			<n-button secondary type="success" @click="showModal=true">自定义遥测</n-button>
			<n-button v-if="0" secondary type="success" @click="router.push('/chart')">跳转到图表</n-button>
			<n-text class="ms-auto" depth="3">遥测版本: {{ selectedVersion[props.device.asLabel]["telemetry"] }}
			</n-text>
			<n-divider vertical class="mx-0"/>
			<n-text depth="3">最后刷新时间: {{ props.lastTime || latestTelemetryData?.dateTime }}
			</n-text>
			<n-modal
				v-model:show="showModal"
				preset="dialog"
				type="info"
				title="设置自定义遥测"
				positive-text="确认"
				negative-text="算了"
				class="w-1/2"
				content-class="flex flex-col gap-2"
				@positive-click="handlePositive"
			>
				<n-space align="center">
					<n-text type="info" class="text-base">自定义遥测:</n-text>
					<n-tag type="default" v-for="item in profile.filterList" closable
						   @close="() => handleClose(item.label)">
						{{ item.label }}
					</n-tag>
				</n-space>
				<n-input class="w-48" v-model:value="localFilter.label" placeholder="请输入自定义名称"></n-input>
				<n-transfer
					v-model:value="localFilter.value"
					:options="transferOptions"
					source-filterable
				/>
			</n-modal>
		</n-space>
		<div class="h-full">
			<n-data-table
				ref="tableRef"
				:row-key="row =>row.label"
				virtual-scroll
				bordered
				:single-line="false"
				:single-column="false"
				@update:filters="handleUpdateFilter"
				:row-class-name="rowClassName"
				flex-height
				class="h-full w-full"
				:columns="telemetryColumns"
				:data="mutableData"
				size="medium"
				:row-props="rowProps"
			/>
			<n-dropdown
				placement="bottom-start"
				trigger="manual"
				:x="xRef"
				:y="yRef"
				:options="options"
				:show="showDropdown"
				:on-clickoutside="onClickoutside"
				@select="handleSelect"
			/>
		</div>
	</div>
</template>

<style scoped>
.n-scrollbar-rail__scrollbar {
	width: 7px !important;
}

.n-scrollbar-rail__scrollbar:hover {
	width: 10px !important;
}

:deep(.brown td) {
	background-color: brown !important;
}

:deep(.n-data-table-tr td) {
	transition: background-color ease-in-out 0.5s;
}

.fade-move,
.fade-enter-active,
.fade-leave-active {
	transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
}

.fade-enter-from,
.fade-leave-to {
	opacity: 0;
	transform: scaleY(0.01) translate(30px, 0);
}

.fade-leave-active {
	position: absolute;
}
</style>